package cn.weiron.fun.infrastructure;


import cn.weiron.fun.infrastructure.auth.Authority;
import cn.weiron.fun.infrastructure.dto.LoggedAccountDto;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * <p>医政卫监平台.平台服务.SDK：</p>
 * <p>防腐层：账户接口</p>
 *
 * @author 马强
 */
public class AccountAuthorityHandler implements Authority.AccountHandler {
    /**
     * Token Key
     */
    private String tokenKey = "Authorization";

    /**
     * Account service
     */
    private AccountLoginRepository repository;

    /**
     * Get the account object through web request
     *
     * @param request web request
     * @return current account object
     */
    @Override
    public Authority.Account get(HttpServletRequest request) {
        //Token
        String token = getParameterFrom(request, tokenKey);
        //Get
        return get(token, LoggedAccountDto.class.getSimpleName());
    }

    /**
     * Get the account object through token string
     *
     * @param fromToken token string
     * @param forClass  class name
     * @return current account object
     */
    @Override
    public Authority.Account get(String fromToken, String forClass) {
        if (!LoggedAccountDto.class.getSimpleName().equals(forClass)) return null;
        return repository.exist(fromToken).orElse(null);
    }

    /**
     * @param request web request
     * @return token string
     */
    @Override
    public String token(HttpServletRequest request) {
        return getParameterFrom(request, tokenKey);
    }

    /**
     * @param request web request
     * @return account object type string
     */
    @Override
    public String type(HttpServletRequest request) {
        return LoggedAccountDto.class.getSimpleName();
    }

    /**
     * Store the account object into the redis
     *
     * @param account current account object
     */
    @Override
    public void store(HttpServletResponse response, Authority.Account account) {
        assert null != account;
        String token = account.customToken();
        if (token != null) {
            //Cache
            repository.store(account);
            //Cookie
            response.addCookie(new Cookie(tokenKey, token));
            //Header
            response.addHeader(tokenKey, token);
        }
    }

    /**
     * Remove the account object
     *
     * @param request  web request
     * @param response web response
     */
    @Override
    public void remove(HttpServletRequest request, HttpServletResponse response) {
        //Token
        String token = getParameterFrom(request, tokenKey);
        if (null != token && !"".equals(token)) {
            //Remove Cache
            repository.exist(token).ifPresent(accountDto -> repository.clear(accountDto));
            //Remove Header
            response.setHeader(tokenKey, null);
            //Remove Cookie
            response.addCookie(new Cookie(tokenKey, null));
        }
    }

    /**
     * Get the parameter value named paramName from http request.
     * From header/cookie/parameter
     *
     * @param request   web http request
     * @param paramName parameter name
     * @return the parameter value
     */
    private String getParameterFrom(HttpServletRequest request, String paramName) {
        //Get the value from the web request header
        String result = request.getHeader(paramName);

        //If value is null,get from cookie
        if (null == result || result.isEmpty()) {
            if (request.getCookies() != null) {
                for (Cookie cookie : request.getCookies()) {
                    if (paramName.equalsIgnoreCase(cookie.getName())) {
                        result = cookie.getValue();
                        break;
                    }
                }
            }
        }

        //If value is null,get from request parameter
        if (null == result || "".equals(result)) {
            result = request.getParameter(paramName);
        }

        return result;
    }

    public void setTokenKey(String tokenKey) {
        this.tokenKey = tokenKey;
    }

    public void setRepository(AccountLoginRepository repository) {
        this.repository = repository;
    }
}